package com.jt.common.collection;

/**
 * 自定一个阻塞式队列容器
 * 数据存储结构：有界数组
 * 存取规则：FIFO
 * 放数据规则：永远放在size位置，满了则阻塞
 * 取数据规则：永远从下标为0的位置取数据，空了则阻塞
 */
class BlockQueueContainer<T>{
      /**存储数据的容器*/
      private Object[] array;
      /**记录有效元素个数*/
      private int size;
      public BlockQueueContainer(int cap){
            array=new Object[cap];
      }
      /**用于向容器放数据,假如容器满了要阻塞
       * 1)阻塞方法用wait()，wait()方法执行后会释放锁
       * 2)唤醒方法用notifyAll()
       * 说明：wait()和notifyAll()方法必须应用在同步代码块或同步方法中，
       * 由对象锁(同步锁)来调用
       * */
      public synchronized void put(T t){
          //1.判定容器是否已满，满了则阻塞当前线程
          while(size==array.length)
                try{this.wait();}catch (Exception e){}
          //2.在size位置放数据
          array[size]=t;
          //3.有效元素个数加1(也是确定下一个元素的位置)
          size++;
          //4.通知取数据的线程(唤醒处于wait状态的线程)可以取数据了
          this.notifyAll();
      }
      /**用于从容器去数据*/
      public synchronized T take(){
          //1.假如容器已空，则让取数据的线程阻塞
          while(size==0)
                try{this.wait();}catch (Exception e){}
          //2.从下标0位置开始取
          Object data=array[0];
          //3.移动元素(将下标0后面的元素依次前移)
          System.arraycopy(array, 1, array, 0, size-1);
          //4.有效元素个数减一
          size--;
          //5.通过放数据的线程(唤醒放数据时处于阻塞的线程)可以继续放了
          this.notifyAll();
          return (T)data;
      }
}

public class MyBlockQueueTests {
      public static void main(String[] args) {
            BlockQueueContainer<Integer> bqc=
                    new BlockQueueContainer<>(2);
            Thread t1=new Thread(new Runnable() {
                  @Override
                  public void run() {
                        int i=1;
                        while(true){
                            bqc.put(i++);
                            try{Thread.sleep(1000);}
                            catch (Exception e){}
                        }
                  }
            });
            Thread t2=new Thread(new Runnable() {
                  @Override
                  public void run() {
                        while(true){
                              Object data=bqc.take();
                              System.out.println(data);
                              try{Thread.sleep(1000);}
                              catch (Exception e){}
                        }
                  }
            });
            t1.start();
            t2.start();

      }
}
